package com.dongnao.network.nio;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;

/**
 * NIO下的UDP用户数据报
 *
 * DatagramChannel类的静态open()方法返回一个DatagramChannel对象，
 * 每个DatagramChannel对象都关联了一个DatagramSocket对象，
 * DatagramChannel对象的socket()方法返回这个DatagramSocket对象。
 *
 * DatagramChannel对象创建后，与它关联的DatagramSocket对象还没有绑定到任何地址，
 * 必须调用DatagramSocket对象的bind()方法来与一个本地地址绑定。
 *
 * 与DatagramSocket一样，DatagramChannel的connect(SocketAddress remote)方法使得通道只能对特定的远程地址收发数据报。
 * DatagramChannel的isConnected()方法判断通道是否只能对特定的远程地址收发数据报。
 *
 * 只要调用DatagramChannel的disconnect()方法，就能使得通道能再次对多个远程地址收发数据报。
 *
 *
 *  非阻塞模式需要与Selector(选择器)一起使用，将DatagramChannel（数据报通道）在Selector注册了感兴趣的事件（read/write），
 *  调用Selector.select()方法时，该方法会阻塞直至发生了感兴趣的事件，如接收到数据报（read）或可以发送数据报(write，底层缓冲区可用时)，
 *  select()方法结果并返回SelectionKey,通过SelectionKey的方法 isReadable()或 isWritable()来判断是哪一种操作准备好了，
 *  SelectionKey.channel()返回是哪条数据报通道准备好了（ Selector 上可以注册多个可选择通道如多个  DatagramChannel  或SocketChannel），
 *  再调用 该DatagramChannel的接收或发送方法。
 */
public class DaragramChannelServer {

    public static void main(String[] args) {
        start(9999);
    }

    private static void start(int port) {
        try {
            //打开 DatagramChannel
            DatagramChannel channel = DatagramChannel.open();
            channel.configureBlocking(false); //默认时阻塞模式，设置为非阻塞模式
            //在UDP端口9999上接受数据包
            channel.socket().bind(new InetSocketAddress(port)); //如果不绑定端口，则为随机端口
            int port1 = channel.socket().getPort();
            int localPort = channel.socket().getLocalPort();
            System.out.println("====端口：" + port1); //-1
            System.out.println("====本地端口：" + localPort); //0

            System.out.println("====接受数据：");
            ByteBuffer buf = ByteBuffer.allocate(48);
            buf.clear();
            //receive将接收到的数据包内容复制到指定的Buffer
            //Buffer容不下收到的数据，多出的数据将被丢弃
            channel.receive(buf); //非阻塞模式下，receive方法不阻塞
            //channel.read(buf);

            System.out.println("====收到数据");
            int port2 = channel.socket().getPort();
            int localPort2 = channel.socket().getLocalPort();  //如果绑定了端口，则为绑定的端口，否则为随机端口
            System.out.println("====端口：" + port2); //-1
            System.out.println("====本地端口：" + localPort2); //58542

            //发送数据
            buf.flip();
            while (buf.hasRemaining()) {
                //与tcp建立连接不同，udp是无连接的，发送数据需要指定发送的地址和端口
                int bytesSent = channel.send(buf, new InetSocketAddress("localhost", 8080));
            }

            int port3 = channel.socket().getPort();
            int localPort3 = channel.socket().getLocalPort();
            System.out.println("====端口：" + port3); //-1
            System.out.println("====本地端口：" + localPort3); //58542
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
